package com.jspica.app.messagespam;

import java.sql.*;

import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.provider.*;
import android.telephony.SmsMessage;
import android.util.Log;

//import com.jspica.app.messagespam.BlockedListActivity;
import com.jspica.app.messagespam.BlockedListDAO;

public class Receiver extends BroadcastReceiver {
	
	public static int block_cnt = 0; /* 알림에 표시할 탐지수 */

	@Override
	public void onReceive(Context ctx, Intent intent) {
				
		/* 다른 앱이 브로드캐스트 받지못하도록 LOCK */
		abortBroadcast();
		Log.e("@@@@@@@@","receive SMS");

		/* SMS 메시지 추출 */
		Object messages[]		= (Object[])intent.getExtras().get("pdus");
		SmsMessage smsMessage[] = new SmsMessage[1];
		smsMessage[0]			= SmsMessage.createFromPdu((byte[])messages[0]);

		/* 1. 등록된 사용자 차단안함 */
		if (PreferenceManager.getDefaultSharedPreferences(ctx).getBoolean("chk_pass_regi", true)) {
			if (isRegisterd(ctx, smsMessage[0])) {
				clearAbortBroadcast(); /* 브로드캐스트  UNLOCK */
				return;
			}
		}
		Log.e("@@@@@@@@","checked registerd user");

		/* 3. 화이트리스트 */
		if (PreferenceManager.getDefaultSharedPreferences(ctx).getBoolean("chk_whitelist", true)) {
			if (inWhitelist(ctx, smsMessage[0].getMessageBody())) {
				clearAbortBroadcast(); /* 브로드캐스트  UNLOCK */
				return;				
			}
		}
		Log.e("@@@@@@@@","checked white list");
		
		
		/* 2. 스마트 정책 사용 */
		if (PreferenceManager.getDefaultSharedPreferences(ctx).getBoolean("chk_use_inpolicy", true)) {
			if (isSpam(smsMessage[0].getMessageBody(), smsMessage[0])) { 
				BlockedListDAO blockedListDAO = new BlockedListDAO(ctx); // 자체DB create 또는 open.
				blockedListDAO.insert(smsMessage[0]);
				blockedListDAO.close();
				
				if (PreferenceManager.getDefaultSharedPreferences(ctx).getBoolean("chk_block_noti", true)) {
					/* 알림창 */
					notifySpam(ctx, smsMessage[0].getMessageBody());
				}
				
				/* 차단됨 */
				return;
			}
		}
		Log.e("@@@@@@@@","checked smart policy");
		
		/* 3. 017, 018 식별번호 차단 */
		String tel1 = smsMessage[0].getDisplayOriginatingAddress();
		String tel2 = smsMessage[0].getOriginatingAddress();
		Log.e("@@@@@@@@","tel1: " + tel1);
		Log.e("@@@@@@@@","tel2: " + tel2);
	
		if (PreferenceManager.getDefaultSharedPreferences(ctx).getBoolean("chk_block_070", true)) {
			if (tel1.startsWith("070") || tel2.startsWith("070")) { 
				BlockedListDAO blockedListDAO = new BlockedListDAO(ctx); // 자체DB create 또는 open.
				blockedListDAO.insert(smsMessage[0]);
				blockedListDAO.close();
				
				if (PreferenceManager.getDefaultSharedPreferences(ctx).getBoolean("chk_block_noti", true)) {
					// 알림창
					notifySpam(ctx, smsMessage[0].getMessageBody());
				}
				
				// 차단됨 
				return;
			}
		}
		Log.e("@@@@@@@@","checked 070");
		if (PreferenceManager.getDefaultSharedPreferences(ctx).getBoolean("chk_block_080", true)) {
			if (tel1.startsWith("080") || tel2.startsWith("080")) { 
				BlockedListDAO blockedListDAO = new BlockedListDAO(ctx); // 자체DB create 또는 open.
				blockedListDAO.insert(smsMessage[0]);
				blockedListDAO.close();				
				
				if (PreferenceManager.getDefaultSharedPreferences(ctx).getBoolean("chk_block_noti", true)) {
					// 알림창
					notifySpam(ctx, smsMessage[0].getMessageBody());
				}
				
				// 차단됨
				return;
			}
		}
		Log.e("@@@@@@@@","checked 080");
		
	
		/* 4. 블랙리스트 */
		if (PreferenceManager.getDefaultSharedPreferences(ctx).getBoolean("chk_blacklist", true)) {
			Log.e("@@@@@@@@@@@@@", "black_list CHECK");
			if (inBlacklist(ctx, smsMessage[0].getMessageBody())) {
				BlockedListDAO blockedListDAO = new BlockedListDAO(ctx); // 자체DB create 또는 open.
				blockedListDAO.insert(smsMessage[0]);
				blockedListDAO.close();			
				
				if (PreferenceManager.getDefaultSharedPreferences(ctx).getBoolean("chk_block_noti", true)) {
					// 알림창
					notifySpam(ctx, smsMessage[0].getMessageBody());
				}				
				
				// 차단됨
				return;			
			}
		}
		Log.e("@@@@@@@@","checked blacklist");
		
		/* 통과함 */
		clearAbortBroadcast(); /* 브로드캐스트 UNLOCK */
		Log.e("@@@@@@@@","pass SMS !!!!");
	}

	/* 화이트리스트에 있는 문자가 포함되었는지 확인 */
	private boolean inWhitelist(Context ctx, String text) {
		Cursor cursor = null;
		String key = null;
		BlockedListDAO blockedListDAO = new BlockedListDAO(ctx); // 자체DB create 또는 open.
		try {
			cursor = blockedListDAO.getWhitelist();
		} catch (SQLException e) {}

		if (cursor == null || cursor.getCount() <= 0) {
			return false;
		}
				
		do {
			key = cursor.getString(1);
			Log.e("@@@@@@@@@@", "white keyword: " + key);
			
			if (text.contains(key)) {
				blockedListDAO.close();		
				return true;				
			}
		} while(cursor.moveToNext());
				
		blockedListDAO.close();		
		return false;
	}
	
	/* 블랙리스트에 있는 문자가 포함되었는지 확인 */
	private boolean inBlacklist(Context ctx, String text) {
		Cursor cursor = null;
		String key = null;
		BlockedListDAO blockedListDAO = new BlockedListDAO(ctx); // 자체DB create 또는 open.
		try {
			cursor = blockedListDAO.getBlacklist();
		} catch (SQLException e) {}
		
		//Log.e("@@@@@@@@@@@@", "black count :" + cursor.getCount());

		if (cursor == null || cursor.getCount() <= 0) {
			return false;
		}
				
		do {
			key = cursor.getString(1);
			Log.e("@@@@@@@@@@", "black keyword: " + key);
			
			if (text.contains(key)) {
				blockedListDAO.close();		
				return true;				
			}
		} while (cursor.moveToNext());
				
		blockedListDAO.close();
		return false;
	}
	
	
	/* 주소록에 등록된 번호인지 체크 */
	private boolean isRegisterd(Context ctx, SmsMessage smsMessage) {
		String[] projection;
		Uri contactUri;
		Cursor people;
		
		/* 빈 전화번호인 경우 false */ 
		if (smsMessage.getDisplayOriginatingAddress().trim().equals("") ||
				smsMessage.getOriginatingAddress().trim().equals("")) {
			return false;
		}

		Log.e("@@@@@@@@@@", "displayNumber: " + smsMessage.getDisplayOriginatingAddress());
		projection = new String[] {ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME, ContactsContract.CommonDataKinds.Phone.NUMBER};
		contactUri = Uri.withAppendedPath(ContactsContract.CommonDataKinds.Phone.CONTENT_FILTER_URI, Uri.encode(smsMessage.getDisplayOriginatingAddress()));
		
		if (contactUri != null) {
			people = ctx.getContentResolver().query(contactUri, projection, null, null, null);
		
			if (people != null) {
				people.moveToFirst();
					
				if (people != null && people.getCount() > 0) {
					//Log.e("@@@@@@@@@@", "found: " + people.getString(1));
					return true;
				}
			}
		}

		Log.e("@@@@@@@@@@", "number: " + smsMessage.getOriginatingAddress());
		/* 가끔 안걸러지는 번호가 있어, 다시한번 체크한다. */
		contactUri = Uri.withAppendedPath(ContactsContract.CommonDataKinds.Phone.CONTENT_FILTER_URI, Uri.encode(smsMessage.getOriginatingAddress()));

		if (contactUri != null) {
			people = ctx.getContentResolver().query(contactUri, projection, null, null, null);

			if (people != null) {
				people.moveToFirst();
		
				if (people != null && people.getCount() > 0) {
					return true;
				}
			}
		}
		
		return false;
	}

	/* 스마트 정책 모듈 */
	private boolean isSpam(String text, SmsMessage smsMessage) {
		/* 국내 SMS 최대길이는 80자. 한글은 2자로 계산됨
		 * 주의 : String.length()는 한글이나 영문자나 모두 1자로 계산함
		 */
		
		String org_text = text;
		int before_len;
		
		final String HAN_REGEX = "[\uAC00-\uD7AF\u1100-\u11FF\u3130-\u318F]";
		final String ALNUM_REGEX = "[a-zA-Z0-9]";
		final String BLANK_REGEX = "[\t\n\r ]";
		final String ASCII_REGEX = "[\\u0020-\\u007F]"; /* 아스키가 왜 범위가 95 일까? http://titus.uni-frankfurt.de/unicode/unitestx.htm */
		final String TIME24_REGEX = "([0-1][0-9]|2[0-3]):[0-5][0-9]"; /* 00:00 ~ 23:59 */
		final String DATE_REGEX = "(0[1-9]|1[0-2])/(0[1-9]|[1-2][0-9]|30|31)"; /* 01/01 ~ 12/31 */
		
		int hangul_len;		/* 한글 : 한글자에 2길이 */
		int alnum_len;		/* 숫자, 영문 */
		int blank_len; 		/* whilte space */
		int etc_asc_len;	/* 특수기호 및 타문자 */
		int etc_nonasc_len;
		int total_len;
		

		before_len = text.length();
		text =  text.replaceAll(HAN_REGEX, "");
		
		/* set hangul_len */
		hangul_len = (before_len - text.length()) * 2; /* 한글은 SMS에서 2길이. */
	
		before_len = text.length();
		text = text.replaceAll(ALNUM_REGEX, "");
		
		/* set alnum_len */
		alnum_len = before_len - text.length();
		
		before_len = text.length();
		text = text.replaceAll(BLANK_REGEX, "");
	
		/* set blank_len */
		blank_len = before_len - text.length();
		
		before_len = text.length();
		text = text.replaceAll(ASCII_REGEX, "");
		
		/* set etc_asc_len */
		etc_asc_len = before_len - text.length();
		
		/* set etc_nonasc_len */
		etc_nonasc_len = text.length() * 2; /* ascii가 아닌 특수문자는 2길이로 가정함. */
		
		
		total_len = hangul_len +
				alnum_len +
				blank_len +
				etc_asc_len +
				etc_nonasc_len;
		
		Log.e("@@@@@@@@","hangul: " + hangul_len +
				" alnum: " + alnum_len +				" blank: " + blank_len +				" etc_asc: " + etc_asc_len +				" etc_nonasc: " + etc_nonasc_len +				" total: " + total_len);		
		if (total_len > 80) {
			// 에러 : 메시지 길이가 80을 넘는다.
		}
		
		/**********************/
		/* inspection 
		/**********************/
		
		if (total_len < 60) {
			/* 60자 이상만 스팸 체크 함.
			 * 스팸SMS는 일반적으로 78~80 자로 꽉 채워서 보냄. 여유로 70 으로 설정함 */
			return false;
		}
		
		text =  org_text.replaceAll(TIME24_REGEX, "");
		if (text.compareTo(org_text) != 0) {
			/* 카드 결재문자는 시간을 포함한다.
			 * TODO : 현재시간과 비교하면 더 확실함.
			 * 통과 */
			return false;
		}
		
		text =  org_text.replaceAll(DATE_REGEX, "");
		if (text.compareTo(org_text) != 0) {
			/* 카드 금액납부문자는 날짜를 포함한다.
			 * XX/YY 형태의 날짜는 결재 및 소셜커머스 등 정상적인 문자에 포함되는 경우가 많다.
			 * 통과 */
			return false;
		}		
		
				
		String tel1 = smsMessage.getDisplayOriginatingAddress();
		String tel2 = smsMessage.getOriginatingAddress();

		// 길이 3~6이 전화번호에서 끝자리가 다음과 같을경우 통과.
		String pass_postfix[] = {"114"};

		for (int i = 0; i < pass_postfix.length; i++) {
			if ((tel1.length() >= 3  && tel1.length() <= 6 && tel1.endsWith(pass_postfix[i])) ||
					(tel2.length() >= 3  && tel2.length() <= 6 && tel2.endsWith(pass_postfix[i]))) {
				return false;			
			}			
		}
		
		String cellular_prefix[] = {"010", "011", "016", "017", "018", "019"};
		
		boolean is_cellular = false;
		
		for (int i = 0; i < cellular_prefix.length; i++) {
			if ((tel1.length() >= 3 && tel1.startsWith(cellular_prefix[i])) ||
					(tel2.length() >= 3 && tel2.startsWith(cellular_prefix[i]))) {
				is_cellular = true;			
			}			
		}
		
		if (is_cellular) {
			/* 핸드폰 번호이면 루즈하게 체크 */
			if ((etc_asc_len + etc_nonasc_len >= 10) &&  (blank_len <= 10)) {
				return true;			
			}		
			
		} else {
			/* 기타번호이면 타이트하게 체크 */
			if ((etc_asc_len + etc_nonasc_len >= 10) ||  (blank_len <= 10)) {
				return true;				
			}			
		}
						
		/* 나머지는 스팸 아님 */
		return false;
	}

	/* 알림창 등록 */
	void notifySpam(Context ctx, String text) {
		Notification noti = null;
		NotificationManager mNotiManager = null;
		
		noti = new Notification(R.drawable.noti,
				ctx.getString(R.string.block) + " : " + 
				((text.length() > 10) ? text.substring(0, 10) : text) + "...",
				System.currentTimeMillis());
		//noti.defaults |= Notification.DEFAULT_VIBRATE;
		noti.flags |= Notification.FLAG_AUTO_CANCEL;
		
		noti.setLatestEventInfo(ctx, ctx.getString(R.string.app_name),
				ctx.getString(R.string.blocked_msg) + " " + (++block_cnt) + ctx.getString(R.string.gun), pending(ctx));
		
		mNotiManager = (NotificationManager)ctx.getSystemService(Activity.NOTIFICATION_SERVICE);
		mNotiManager.notify(1, noti);
	}
	
	private PendingIntent pending(Context ctx) {
		Intent intent = null;
		PendingIntent content = null;
	
		intent = new Intent(ctx, MessageSpam.class);
		intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
		
		content = PendingIntent.getActivity(ctx, 0, intent, 0);
		
		return content;
	}

}